import { SseResponseEventEnum } from './constants';
import { getErrText } from './utils';
import type { ChatHistoryItemResType } from './constants';
import type { StartChatFnProps } from './constants';
import { DispatchNodeResponseKeyEnum } from './constants';
import { EventStreamContentType, fetchEventSource } from '@fortaine/fetch-event-source';
import { formatTime2YMDHMW } from './time';
import { ResultEnum } from '@/types/enum';

type StreamFetchProps = {
  url?: string;
  data: Record<string, any>;
  onMessage: StartChatFnProps['generatingMessage'];
  abortCtrl: AbortController;
};
export type StreamResponseType = {
  responseText: string;
  [DispatchNodeResponseKeyEnum.nodeResponse]: ChatHistoryItemResType[];
};
class FatalError extends Error {}

export const streamFetch = ({
  url = '/openapi/v1/chat/completions',
  // url = '/api/v1/chat/completions',
  data,
  onMessage,
  abortCtrl,
}: StreamFetchProps) =>
  new Promise<StreamResponseType>(async (resolve, reject) => {
    const timeoutId = setTimeout(() => {
      abortCtrl.abort('Time out');
    }, 60000);

    // response data
    let responseText = '';
    let responseQueue: (
      | {
          event:
            | SseResponseEventEnum.fastAnswer
            | SseResponseEventEnum.answer
            | SseResponseEventEnum.contentViolation;
          text?: string;
          think?: string;
        }
      | {
          event:
            | SseResponseEventEnum.toolCall
            | SseResponseEventEnum.toolParams
            | SseResponseEventEnum.toolResponse;
          [key: string]: any;
        }
    )[] = [];
    let errMsg: string | undefined;
    let responseData: ChatHistoryItemResType[] = [];
    let finished = false;

    let endThink = true;

    const finish = () => {
      if (errMsg !== undefined) {
        return failedFinish();
      }
      return resolve({
        responseText,
        responseData,
      });
    };

    const failedFinish = (err?: any, status?: number) => {
      finished = true;
      reject({
        status: status ? status : 200,
        message: getErrText(err, errMsg ?? '响应过程出现异常~'),
        responseText,
      });
    };

    const isAnswerEvent = (event: SseResponseEventEnum) =>
      event === SseResponseEventEnum.answer || event === SseResponseEventEnum.fastAnswer;
    // animate response to make it looks smooth
    function animateResponseText() {
      // abort message
      if (abortCtrl.signal.aborted || finished) {
        responseQueue.forEach((item) => {
          onMessage(item);
          if (isAnswerEvent(item.event)) {
            responseText += item.text;
          }
        });
        return finish();
      }

      if (responseQueue.length > 0) {
        const fetchCount = Math.max(1, Math.round(responseQueue.length / 30));
        for (let i = 0; i < fetchCount; i++) {
          const item = responseQueue[i];
          onMessage(item);
          if (isAnswerEvent(item.event)) {
            responseText += item.text;
          }
        }

        responseQueue = responseQueue.slice(fetchCount);
      }

      if (finished && responseQueue.length === 0) {
        return finish();
      }

      requestAnimationFrame(animateResponseText);
    }
    // start animation
    animateResponseText();

    try {
      // auto complete variables
      const variables = data?.variables || {};
      variables.cTime = formatTime2YMDHMW();

      const requestData = {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-RUIGPT-Source': 'share-link',
        },
        signal: abortCtrl.signal,
        body: JSON.stringify({
          ...data,
          variables,
          detail: true,
          stream: true,
        }),
      };

      // send request
      await fetchEventSource(url, {
        ...requestData,
        async onopen(res) {
          clearTimeout(timeoutId);
          const contentType = res.headers.get('content-type');

          // not stream
          if (contentType?.startsWith('text/plain')) {
            return failedFinish(await res.clone().text());
          }

          // failed stream
          if (
            !res.ok ||
            !res.headers.get('content-type')?.startsWith(EventStreamContentType) ||
            res.status !== 200
          ) {
            try {
              if (res.status === ResultEnum.UNAUTHORIZED) {
                let errText = await res.clone().text();
                let errJson = await res.clone().json();
                if (errJson?.code == ResultEnum.NOT) {
                  failedFinish(errJson?.message, ResultEnum.UNAUTHORIZED);
                } else {
                  failedFinish(errText, ResultEnum.UNAUTHORIZED);
                }
              }
              failedFinish(await res.clone().json());
            } catch {
              const errText = await res.clone().text();
              if (!errText.startsWith('event: error')) {
                failedFinish();
              }
            }
          }
        },

        onmessage({ event, data }) {
          if (data === '[DONE]') {
            return;
          }

          // parse text to json
          const parseJson = (() => {
            try {
              return JSON.parse(data);
            } catch (error) {
              return {};
            }
          })();

          if (event === SseResponseEventEnum.contentViolation) {
            console.log(event, data, event === SseResponseEventEnum.contentViolation);
            responseQueue.push({
              event,
              text: data,
            });
            return;
          }

          if (
            event === SseResponseEventEnum.answer ||
            event === SseResponseEventEnum.contentViolation
          ) {
            const text = parseJson.choices?.[0]?.delta?.content || '';
            for (const item of text) {
              responseQueue.push({
                event,
                text: item,
              });
            }
            return;

            // console.log("parseJson=======",text);
            // if(text === '<think>'){
            //   console.log('这是think开始');
            //   endThink = false
            // }

            // if(endThink == false){
            //   if(text != '<think>'&& text != '</think>'){
            //     for (const item of text) {
            //     responseQueue.push({
            //       event,
            //       think: item,
            //     });
            //   }
            //   }else{
            //   }
            // }

            // if(text === '</think>'){
            //   console.log('这是think结束');
            //   endThink = true
            // }

            // if(endThink == true){
            //   for (const item of text) {
            //     responseQueue.push({
            //       event,
            //       text: item,
            //     });
            //   }
            // }
          } else if (event === SseResponseEventEnum.fastAnswer) {
            const text = parseJson.choices?.[0]?.delta?.content || '';
            responseQueue.push({
              event,
              text,
            });
          } else if (
            event === SseResponseEventEnum.toolCall ||
            event === SseResponseEventEnum.toolParams ||
            event === SseResponseEventEnum.toolResponse
          ) {
            responseQueue.push({
              event,
              ...parseJson,
            });
          } else if (event === SseResponseEventEnum.flowNodeStatus) {
            onMessage({
              event,
              ...parseJson,
            });
          } else if (event === SseResponseEventEnum.flowResponses && Array.isArray(parseJson)) {
            responseData = parseJson;
            endThink = true;
          } else if (event === SseResponseEventEnum.updateVariables) {
            onMessage({
              event,
              variables: parseJson,
            });
          } else if (event === SseResponseEventEnum.error) {
            errMsg = getErrText(parseJson, '流响应错误');
            const text = parseJson.choices?.[0]?.delta?.content || '';
            responseQueue.push({
              event,
              text,
            });
          } else if (event === SseResponseEventEnum.interactive) {
            console.log('interactive', parseJson);

            const text = parseJson.choices?.[0]?.delta?.content || '';
            responseQueue.push({
              event,
              text,
            });
          }
        },
        onclose() {
          finished = true;
        },
        onerror(err) {
          console.log('aaa');
          if (err instanceof FatalError) {
            throw err;
          }
          clearTimeout(timeoutId);
          failedFinish(getErrText(err));
        },
        openWhenHidden: true,
      });

      console.log(555);
    } catch (err: any) {
      console.log('aaa11');

      console.log(err);

      clearTimeout(timeoutId);

      if (abortCtrl.signal.aborted) {
        finished = true;

        return;
      }
      failedFinish(err);
    }
  });
